import logging
import os
import time
from typing import Annotated

import httpx
from bs4 import BeautifulSoup
from dotenv import find_dotenv, load_dotenv
from langchain_tavily.tavily_search import TavilySearch
from mcp.server.fastmcp import FastMCP
from selenium import webdriver
from selenium.webdriver.remote.remote_connection import LOGGER

mcp = FastMCP("travel_server", host="localhost", port=8802, debug=True)


@mcp.tool()
def get_coordinate(
    address: Annotated[str, "结构化地址信息"],
    city: Annotated[str, "要获取经纬度的地点所在的城市名称,可不填"] = "",
):
    """位置获取工具。根据地点名称和城市名称获取该地点的经纬度。由于可能存在同名地点，
    所以返回的是一个包含所有同名地点详细地址和经纬度的列表"""

    amap_key = os.getenv("AMAP_API_KEY")

    base_url = r"https://restapi.amap.com/v3/geocode/geo"
    params = {
        "key": amap_key,
        "address": address,
        "city": city,
    }

    response = httpx.get(base_url, params=params).json()

    if response["status"] == "0":
        error_msg = f"获取以下地点坐标失败：{address}。错误信息: {response['info']}。"
        return error_msg

    results = []

    for geocode in response["geocodes"]:
        results.append(
            {
                "address": geocode["formatted_address"],
                "coordinate": geocode["location"],
                "city_code": geocode["citycode"],
            }
        )

    return results


@mcp.tool()
def nearby(
    location: Annotated[str, "中心点坐标，以“,”分割,经度在前,纬度在后,如117.500244,40.417801"],
    city_code: Annotated[str, "城市编码"],
    types: Annotated[
        str,
        """查询POI类型。多个类型用“|”分割。如果查询住宿,只能填写以下类型:“宾馆酒店”“旅馆招待所”。如果查询餐饮,只能填写
        以下类型:“中餐厅”“外国餐厅”“快餐厅”。关键字和POI类型二者至少填写一个。""",
    ] = "",
    keyword: Annotated[
        str,
        """要搜索的关键字。除非用户有特别指定具体的地点名称时才填写,如“星巴克”“希尔顿”等,否则不要填写!一次只能搜索一个
        关键字。关键字和POI类型二者至少填写一个。""",
    ] = "",
    radius: Annotated[int, "查询半径，单位米。"] = 5000,
    offset: Annotated[int, "每页搜索结果数目。"] = 20,
    page: Annotated[int, "当前页数。"] = 1,
):
    """周边搜索工具。根据中心点坐标和关键字或POI类型搜索周边POI。返回结果中,距离的单位都是米,费用的单位都是元。"""
    key = os.getenv("AMAP_API_KEY")
    url = "https://restapi.amap.com/v3/place/around"
    request_params = {
        "key": key,
        "location": location,
        "city": city_code,
        "types": types,
        "keyword": keyword,
        "radius": radius,
        "offset": offset,
        "page": page,
    }

    response = httpx.get(url, params=request_params).json()

    result = {"center": location, "suggest_count": response["count"], "pois": []}

    for poi in response["pois"]:
        result["pois"].append(
            {
                "name": poi["name"],
                "type": poi["type"],
                "address": poi["address"],
                "distance": poi["distance"],
                "location": poi["location"],
                "rating": poi["biz_ext"]["rating"] if "biz_ext" in poi else "not available",
                "cost": poi["biz_ext"]["cost"] if "biz_ext" in poi else "not available",
            }
        )

    return result


@mcp.tool()
def get_static_map(
    location: Annotated[str, "中心点坐标。规则：经度和纬度用','分隔 经纬度小数点后不得超过6位。"],
    zoom: Annotated[int, "地图缩放级别,范围:从大到小为1-17"] = 12,
) -> str:
    """返回一张坐标点周围的地图图片"""

    amap_key = os.getenv("AMAP_API_KEY")

    base_url = r"https://restapi.amap.com/v3/staticmap"
    params = {"key": amap_key, "location": location, "zoom": zoom}

    response = httpx.get(base_url, params=params)

    try:
        if (r := response.json())["status"] == "0":
            error_msg = f"获取以下地图失败：{location}。错误信息: {r['info']}。"
            return error_msg

    except Exception:
        pass

    img_data = response.content

    file_name = location.replace(".", "_").replace(",", "_") + ".png"
    file_path = os.path.join(os.getcwd(), "static\\map_pngs", file_name)

    with open(file_path, "wb") as f:
        f.write(img_data)

    return file_name


@mcp.tool()
def route_planning(
    origin: Annotated[str, "出发点的经纬度,小数点后不超过6位,以,号分割,经度在前纬度在后"],
    destination: Annotated[str, "出发地的经纬度,小数点后不超过6位,以,号分割,经度在前纬度在后"],
    origin_city_code: Annotated[str, "出发点的城市编码"],
    dest_city_code: Annotated[str, "目的地城市编码.非必填,跨城市必填."] = "",
):
    """路线规划工具。规划综合各类公共交通方式（火车、公交、地铁）的交通方案，返回从出发点到目的地的步行距离、
    出租车费用以及公共交通方案列表。返回结果中，距离的单位都是米，时间的单位都是秒，费用的单位都是元。
    如果返回的公共交通方案列表为空，说明两个地点之间没有可用的公共交通方式。"""

    url = r"https://restapi.amap.com/v3/direction/transit/integrated"
    amap_key = os.getenv("AMAP_API_KEY")

    params = {
        "origin": origin,
        "destination": destination,
        "city": origin_city_code,
        "cityd": dest_city_code,
        "key": amap_key,
    }

    response = httpx.get(url, params=params).json()

    if response["status"] == "0":
        raise ValueError(
            f"获取从{origin}到{destination}的交通方案失败。请检查出发点、目的地和城市编码是否正确。错误信息: {response['info']}"
        )

    routes = {
        "origin": response["route"]["origin"],
        "destination": response["route"]["destination"],
        "walking_distance": response["route"]["distance"],
        "taxi_cost": response["route"]["taxi_cost"]
        if response["route"]["taxi_cost"] != "0"
        else "not available",
        "public_transport_options_list": [],
    }

    for item in response["route"]["transits"]:
        routes["public_transport_options_list"].append(
            {"cost": item["cost"], "duration": item["duration"], "walking_distance": item["walking_distance"]}
        )

    return routes


@mcp.tool()
def web_search(
    query: Annotated[str, "要联网搜索的问题"], num_results: Annotated[int, "要搜索出的结果数"] = 3
):
    """对于无法回答的问题,尝试使用该联网搜索工具"""
    searcher = TavilySearch(num_results=num_results)

    return searcher.invoke(query)["results"]


def InitWebDriver():
    # 设置日志等级
    LOGGER.setLevel(logging.WARNING)
    # 使用chrome开发者模式
    options = webdriver.ChromeOptions()
    options.add_experimental_option("excludeSwitches", ["enable-automation"])

    # 禁用启用Blink运行时的功能
    options.add_argument("--disable-blink-features=AutomationControlled")

    # Selenium执行cdp命令  再次覆盖window.navigator.webdriver的值
    driver = webdriver.Chrome(options=options)
    driver.execute_cdp_cmd(
        "Page.addScriptToEvaluateOnNewDocument",
        {
            "source": """
                        Object.defineProperty(navigator, 'webdriver', {
                          get: () => undefined
                        })
                      """
        },
    )
    return driver


def fetch_page_with_selenium(driver, url):
    driver.get(url)
    # 等待页面完全加载
    # WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.TAG_NAME, "body")))
    time.sleep(2)
    page_content = driver.page_source
    return page_content


def get_destination_overview(soup):
    overview_element = soup.find("span", {"id": "mdd_poi_desc"})
    if overview_element:
        return overview_element.text.strip()
    else:
        return "Overview not found."


def get_scenic_spots(driver, soup):
    spots = []
    # 获取景点列表
    spot_elements = soup.find_all("ul", class_="scenic-list clearfix")
    for spot in spot_elements[:1]:  # 只取第一页
        for item in spot.find_all("li"):
            spot_name = item.find("h3").text.strip()
            spot_url = item.find("a")["href"]
            spot_details = get_scenic_spot_details(driver, "https://www.mafengwo.cn" + spot_url, spot_name)
            spots.append(spot_details)
    return spots


def get_scenic_spot_details(driver, url, spot_name):
    page_content = fetch_page_with_selenium(driver, url)
    soup = BeautifulSoup(page_content, "html.parser")

    summary = (
        soup.find("div", class_="summary").text.strip()
        if soup.find("div", class_="summary")
        else "No summary available."
    )
    duration = (
        soup.find("li", class_="item-time").find("div", class_="content").text.strip()
        if soup.find("li", class_="item-time")
        else "No duration info."
    )

    open_time = "No open time info."
    for dt in soup.find_all("dt"):
        if "开放时间" in dt.text:
            open_time = dt.find_next("dd").text.strip()
            break

    return {"name": spot_name, "summary": summary, "duration": duration, "open_time": open_time}


@mcp.tool()
def get_attractions_information(
    destination: Annotated[str, "目的地名称，必须是一个明确的城市或村镇名称"],
) -> dict:
    """景点搜索工具。获取目的地概览和景点信息列表"""
    _ = load_dotenv(find_dotenv())
    driver = InitWebDriver()
    base_search_url = "https://www.mafengwo.cn/search/q.php"
    search_url = f"{base_search_url}?q={destination}"
    print("search_url:", search_url)
    soup = BeautifulSoup(fetch_page_with_selenium(driver, search_url), "html.parser")
    more_link = soup.find("a", text="查看更多相关旅行地>>")["href"]
    print(more_link)

    # 提取 mddid 并拼接目标链接
    mddid = more_link.split("mddid=")[1].split("&")[0]
    destination_url = f"https://www.mafengwo.cn/jd/{mddid}/gonglve.html"

    destination_page = fetch_page_with_selenium(driver, destination_url)
    soup = BeautifulSoup(destination_page, "html.parser")

    overview = get_destination_overview(soup)
    print("overview:", overview)
    scenic_spots = get_scenic_spots(driver, soup)
    print("scenic_spots:", scenic_spots)

    result = {"overview": overview, "scenic_list": scenic_spots}
    driver.quit()
    return result


if __name__ == "__main__":
    from dotenv import find_dotenv, load_dotenv

    _ = load_dotenv(find_dotenv())

    mcp.run("stdio")
